package myLearnjdk.jdk.javax.script;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;









/**
 * 入口类
 * @author yangcheng  
 * @date 2019年11月4日  
 * @version V1.0
 */
public class TestMain {
	public static void main(String[] args) throws ScriptException, NoSuchMethodException {
//		testContext();
//		testJavaInterfaceExecuteJS();//
		
//		testJavaInterfaceExecuteCompiledJS();//494
		testJSImportJava();
		
		
		
		
		
		
	}
	
	/**
	 * @throws ScriptException 
	 * @throws NoSuchMethodException 
	 * 
	 */
	public static void testJSImportJava() throws ScriptException, NoSuchMethodException{
		//获得脚本执行引擎对象
		ScriptEngineManager engineManager = new ScriptEngineManager();
		ScriptEngine scriptEngine=engineManager.getEngineByName("nashorn");
		/**
		 * 当
		 */
		String script = 
						"function finalAssembly(datas,data2,data3){"+
//						+ "print('datas的类型为=='+Object.prototype.toString.call(datas));" +
						"  print('datas的类型为=='+datas.valueOf());" +
						"  print('datas的类型为=='+data2.valueOf());" +
						"  print('datas的类型为=='+data3.valueOf());" +
						"	var newDatas = new Array();" +
						"	var len = datas.length ;" +
						"	newDatas.push(datas.length);" +
						"	for(var i = 0 ; i < len ; i++){" +
						"		newDatas.push(datas[i]);" +
						"	}"
						+ "return newDatas ;" +
						" }";
//		String script = 
//				"function finalAssembly(datas){" +
//						"		    var newDatas = new Array();" +
//						"		    var len = datas.length ;" +
//						"		    newDatas.push(datas.length);" +
//						"		    for(var i = 0 ; i < len ; i++){" +
//						"		        newDatas.push(datas[i]);" +
//						"			 }"
//						+ "return newDatas ;" +
//						" }";
		scriptEngine.eval(script);
		Invocable jsinvocable =(Invocable) scriptEngine;
		List<Number> list = new ArrayList<>();
		list.add(1);
		list.add(-2);
		list.add(2.06);
		Object object = jsinvocable.invokeFunction("finalAssembly",list.toArray() /*new Number[]{1,2,22.01}*/);
		
		JSONObject strMap = (JSONObject) JSON.toJSON(object);
		int dataSize = strMap.size();
		byte[] newData = new byte[dataSize];
		for(int i = 0 ; i < dataSize ; i++){
			newData[i] =  ( Integer.valueOf(String.valueOf(strMap.get(String.valueOf(i))))).byteValue();
		}
		for (byte b : newData) {
			System.out.println(b);
		}
	}
	
	
	/**
	 * 脚本基本用法
	 * @throws FileNotFoundException
	 * @throws ScriptException
	 * @throws NoSuchMethodException
	 */
	public void basicTest() throws FileNotFoundException, ScriptException, NoSuchMethodException{
		//获得脚本执行引擎对象
		ScriptEngineManager engineManager = new ScriptEngineManager();
		ScriptEngine scriptEngine=engineManager.getEngineByName("javascript");
		//定义变量并存储到引擎上下文中
		scriptEngine.put("msg", "jerry");
		System.out.println("msg="+scriptEngine.get("msg"));
		//通过字符串定义脚本  并通过引擎的eval()方法执行。
		
		String str = " var man ={name : 'yc' , age : 20 }; " ;
		str += "println(man.name);" ;
		scriptEngine.eval(str);
		
		//直接通过js也可以修改引擎对象上下文中变量的值
		String str1 = "msg = 'tom' ;";
		scriptEngine.eval(str1);//eval（）方法执行脚本内容
		System.out.println("msg="+scriptEngine.get("msg"));
		
		/**
		 * 定义带参数函数并执行----可动态传参并获取返回值
		 */
		//1.定义函数
		String str2 = "function add (a , b) { return a+b; } ";
		scriptEngine.eval(str2);
		//2.取得调用接口
		Invocable jsinvocable =(Invocable) scriptEngine;
		//3.执行脚本中定义的方法
		Object result=jsinvocable.invokeFunction("add", 1,2);
		System.out.println("result="+result);
		
		/**
		 * js中引入java包 并使用相关类
		 */
		String str3="importPackage(java.util); var list = Arrays.asList(['a','v','c']);";
		scriptEngine.eval(str3);
		List list=(List) scriptEngine.get("list");
		for (Object object : list) {
			System.out.println(object);
		}
		//直接加载.js文件  
		URL url=TestMain.class.getClassLoader().getResource("test.js");
		scriptEngine.eval(new FileReader(url.getPath()));
		
	}
	
	/**
	 * 測試引擎上下文
	 * @throws ScriptException
	 */
	public static void testContext() throws ScriptException{
		
		long start = System.currentTimeMillis();
		ScriptEngineManager manager = new ScriptEngineManager();
		ScriptEngine engine = manager.getEngineByName("js");
		ScriptEngine engine2 = manager.getEngineByName("js");
		
		/**
		 * 脚本引擎默认是解释执行的，如果需要反复执行脚本，可以使用它的可选接口Compilable来编译执行脚本，以获得更好的性能
		 */
		engine.eval("function add_num (a , b) { return a+b; } ");//执行编译之后的脚本
		Invocable jsinvocable =(Invocable) engine;
		Caculate caculate2 = jsinvocable.getInterface(Caculate.class);
		caculate2.add_num(1, 2);
		
		
		
		
		
	}
	
	
	/**
	 * 声明js脚本 实现java接口的方法，并通过java中的接口类调用特定方法---方法每次都要执行脚本
	 * @throws ScriptException
	 */
	public static void testJavaInterfaceExecuteJS() throws ScriptException{
		long start = System.currentTimeMillis();
		ScriptEngineManager manager = new ScriptEngineManager();
		ScriptEngine engine = manager.getEngineByName("js");
		
//		Invocable jsInvoke = (Invocable) engine;
		
		String str2 = "function add_num (a , b) { return a+b; } ";
		engine.eval(str2);
		//2.取得调用接口---通过Invocable接口来多次调用脚本库中的函数
		Invocable jsinvocable =(Invocable) engine;
		int sum = 0 ;
		for(int i = 0 ; i < 100000 ; i ++){
			Caculate caculate = jsinvocable.getInterface(Caculate.class);
			sum += caculate.add_num(1, 0);
//			System.out.println("通过java的接口类执行js脚本结果=="+caculate.add_num(1, 2));;
		}
		System.out.println("执行用时 = "+(System.currentTimeMillis() - start)+";sum = "+sum);
	}
	/**
	 * 编译js脚本之后再调用--提高运行效率
	 * @throws ScriptException
	 */
	public static void testJavaInterfaceExecuteCompiledJS() throws ScriptException{
		
		long start = System.currentTimeMillis();
		ScriptEngineManager manager = new ScriptEngineManager();
		ScriptEngine engine = manager.getEngineByName("js");
		
		/**
		 * 脚本引擎默认是解释执行的，如果需要反复执行脚本，可以使用它的可选接口Compilable来编译执行脚本，以获得更好的性能
		 */
		Compilable compEngine = (Compilable) engine;
		CompiledScript compiledScript = compEngine.compile("function add_num (a , b) { return a+b; } ");
		compiledScript.eval();//执行编译之后的脚本
		Invocable jsinvocable2 =(Invocable) engine;
		
		int sum = 0;
		for(int i = 0 ; i < 100000 ; i ++){
			Caculate caculate2 = jsinvocable2.getInterface(Caculate.class);
			sum += caculate2.add_num(1, 0);
//			System.out.println("通过java的接口类执行js脚本结果=="+caculate2.add_num(1, 2));;
		}
		
		System.out.println("执行用时 = "+(System.currentTimeMillis() - start)+";sum = "+sum);
	}
	
	
	/**
	 * 同一个ScriptEngine中，如果编译了多个同名方法，则会导致方法丢失，
	 * 后面的方法会覆盖掉之前的同名方法。
	 */
	public static void testmutilFunction(){
		Map<String,CompiledScript> test = new HashMap<>();
		
		ScriptEngineManager manager = new ScriptEngineManager();
		ScriptEngine engine = manager.getEngineByName("js");
		
		/**
		 * 脚本引擎默认是解释执行的，如果需要反复执行脚本，可以使用它的可选接口Compilable来编译执行脚本，以获得更好的性能
		 */
		Compilable compEngine = (Compilable) engine;
		CompiledScript compiledScript;
		try {
			compiledScript = compEngine.compile("function add_num (a , b) { return a+b; } ");
			compiledScript.eval();//执行编译之后的脚本
			test.put("add", compiledScript);
			
			compiledScript = compEngine.compile("function add_num (a , b) { return a/b; } ");
			compiledScript.eval();//执行编译之后的脚本
			test.put("divide", compiledScript);
			
			
			Invocable jsinvocable =(Invocable) test.get("add").getEngine();
			System.out.println("通过java的接口类执行js脚本结果=="+jsinvocable.invokeFunction("add_num", 1, 2));
			jsinvocable =(Invocable) test.get("add").getEngine();
			System.out.println("通过java的接口类执行js脚本结果=="+jsinvocable.invokeFunction("add_num", 6, 2));
		} catch (ScriptException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}






